/**
 ******************************************************************************
 * @file    main.c
 * @author  Hsiang Hsu, Zhenyu Yang
 * @version V0.0.1
 * @date    2022年12月25日(V0.0.1)：初始版本
 *          2023年11月27日(V0.0.2)：修改文件类型，设计LL库程序
 * @brief   主函数
 ******************************************************************************
 **/

/* Includes ------------------------------------------------------------------*/
#include "bmp280.h"
#include "icm20948.h"
#include "main.h"
#include "util.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
SUART suart1 = {0}; // 串口1数据
SDATA sdat = {0};   // 数据暂存
/* Private function prototypes -----------------------------------------------*/
static void SystemClock_Config(void);
uint32_t LL_CRC_Calculate(uint32_t buf[], uint32_t len);
static void DataConvert(SDATA *ldat);
static void SendDataUSB(uint8_t buf[], uint32_t len);
static void SendDataUART(unsigned char *buf, unsigned short len);
static void SendAllToVofa(ICM_Data_t icm_data, BMP280_Data_t bmp_data);
void Send32BitToVofa(float bytes); // 【TODO】：暂且设为非 static 函数，待修改
static void SendDataToVofa(float *pbuf, uint8_t len);
/* Private functions ---------------------------------------------------------*/

/*******************************************************************************
** 名    称 : main
** 功    能 : 主函数
** 入口参数 : 无
** 出口参数 : int
*******************************************************************************/

// buffer init
ICM_Data_t icm_data = {0}; // ICM20948 数据缓存
int main(void)
{

    /* Configure the system clock to 72 MHz */
    SystemClock_Config();

    // 板级驱动
    BSP_Init();

    // TinyUSB
    tud_init(0);

    // ICM20948
    ICM_Init();

    // BMP280
    BMP_Init();

    // buffer init
    BMP280_Data_t bmp_data = {0};                  // BMP280 数据缓存
    ICMInterruptStatusTypeDef icm_intstatus = {0}; // ICM20948 中断标志缓存(testing)
    float vofa_buf[VOFA_DATA_LEN] = {0};           // Vofa 发送缓存

    // 主循环
    while (1)
    {
        // 读取 BMP280 数据
        // BMPGetData(&bmp_data);

        // eg . 发送所有数据到 Vofa 上位机 (结构体)
        SendAllToVofa(icm_data, bmp_data);

        // eg. 发送数据到 Vofa 上位机 (单浮点数)
        // Send32BitToVofa(-0.9);
        // 发送所有传感器数据到 Vofa 上位机 (结构体)

        // eg. 发送数据到 Vofa 上位机 (浮点数组)
        // vofa_buf[0] = vofa_data.acc_z;
        // vofa_buf[1] = vofa_data.acc_y;
        // vofa_buf[2] = 3.62;
        // SendDataToVofa(vofa_buf, 4);

        // 限制串口发送速率，防止发爆缓存
        LL_mDelay(10); // 延时100ms
    }
}

/**
 * @brief  System Clock Configuration
 *         The system Clock is configured as follow :
 *            System Clock source            = PLL (HSE)
 *            PLLSRC                         = LL_RCC_PLLSOURCE_HSE_DIV_1
 *            SYSCLK(Hz)                     = 72000000
 *            HCLK(Hz)                       = 72000000
 *            AHB Prescaler                  = 1
 *            APB1 Prescaler                 = 2
 *            APB2 Prescaler                 = 1
 *            HSE Frequency(Hz)              = 8000000
 *            PLL_MUL                        = 16
 *            Flash Latency(WS)              = 2
 * @param  None
 * @retval None
 */
static void SystemClock_Config(void)
{
    /* Set FLASH latency */
    LL_FLASH_SetLatency(LL_FLASH_LATENCY_2);
    while (LL_FLASH_GetLatency() != LL_FLASH_LATENCY_2)
    {
    }

    /* Enable HSE */
    LL_RCC_HSE_Enable(); // HSE 8MHZ
    while (LL_RCC_HSE_IsReady() != 1)
    {
    };

    /* Main PLL configuration and activation */
    LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE_DIV_1, LL_RCC_PLL_MUL_9); // 8*9 = 72 MHZ
    // /* Enable PLL */
    LL_RCC_PLL_Enable();
    while (LL_RCC_PLL_IsReady() != 1)
    {
    };

    // /* Sysclk activation on the main PLL */
    LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); // AHB 72MHZ
    LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
    /* Set APB1 & APB2 prescaler */
    LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_2); // APB1 36MHZ
    LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1); // APB2 72MHZ
    while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
    {
    };

    /* This code set Systick.LOAD to 72000 which means the Systick.STK_VAL will count down to 0 and asserts an exception per 1 us*/
    SysTick_Config(72000); // SysTick = AHB = 72MHZ

    /* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */
    LL_SetSystemCoreClock(72000000);
}

/*******************************************************************************
** 名    称 : LL_CRC_Calculate
** 功    能 : CRC 计算
** 入口参数 : buf     数据缓存
**           len     数据长度
** 出口参数 : CRC     校验值
*******************************************************************************/
uint32_t LL_CRC_Calculate(uint32_t buf[], uint32_t len)
{
    uint32_t i;

    LL_CRC_ResetCRCCalculationUnit(CRC);
    for (i = 0; i < len; i++)
        LL_CRC_FeedData32(CRC, __RBIT(buf[i]));

    return __RBIT(LL_CRC_ReadData32(CRC)) ^ 0xFFFFFFFF;
}

/*******************************************************************************
** 名    称 : DataConvert
** 功    能 : 数据转换
** 入口参数 : dat     发送缓存
**           xxx     补充项
** 出口参数 : 无
*******************************************************************************/
static void DataConvert(SDATA *ldat)
{
    union FL2UCH fdat;
    unsigned char i;
    unsigned char crc = 0;

    // 字长归0
    ldat->len = 0;

    // 帧头
    ldat->buf[ldat->len] = 0x55;
    ldat->len++;
    ldat->buf[ldat->len] = 0x55;
    ldat->len++;

    // 帧号
    ldat->buf[ldat->len] = 0x00;
    ldat->len++;

    // 数据
    fdat.f = 13.1f;
    ldat->buf[ldat->len] = fdat.c[0];
    ldat->len++;
    ldat->buf[ldat->len] = fdat.c[1];
    ldat->len++;
    ldat->buf[ldat->len] = fdat.c[2];
    ldat->len++;
    ldat->buf[ldat->len] = fdat.c[3];
    ldat->len++;
    // ...

    // CRC 校验，32位，从第三个字节开始，末尾1表示1*4字节，总共4个字节
    *((uint32_t *)&ldat->buf[ldat->len]) = LL_CRC_Calculate((uint32_t *)&ldat->buf[3], 1);
    ldat->len += 4;
}

/*******************************************************************************
** 名    称 : SendDataUSB
** 功    能 : USB 数据发送
** 入口参数 : buf : 数据缓存
**          len : 数据长度
** 出口参数 : 无
*******************************************************************************/
static void SendDataUSB(uint8_t buf[], uint32_t len)
{
    tud_cdc_n_write(0, buf, len);
    tud_cdc_n_write_flush(0);
}

/*******************************************************************************
** 名    称 : SendDataUART
** 功    能 : 串口 数据发送
** 入口参数 : buf : 数据缓存
**          len : 数据长度
** 出口参数 : 无
*******************************************************************************/
static void SendDataUART(unsigned char *buf, unsigned short len)
{
    /* 0. Disable DMA Channel for Rx & Tx */
    /* ATTENTION：
     * 先禁用 DMA 通道，然后再把传输数据拷贝到发送串口缓存，防止不完全拷贝发送
     */
    LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_5);
    LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_4);
    /* 1. Bound DMA Buffer and set data transfer amount */
    suart1.txlen = len;
    memcpy(suart1.txbuf, buf, suart1.txlen);
    LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_4,
                           (uint32_t)suart1.txbuf,
                           LL_USART_DMA_GetRegAddr(USART1),
                           LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_4));
    LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_4, suart1.txlen);
    /* 2. Enable DMA TX & RX request */
    LL_USART_EnableDMAReq_RX(USART1);
    LL_USART_EnableDMAReq_TX(USART1);
    /* 3. Enable DMA Channel for Rx & Tx */
    /* ATTENTION：
     * 每次发送数据前开启一次通道，防止哪里不小心禁了，引发 BUG
     */
    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_5);
    LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_4);
}

/*******************************************************************************
** 名    称 : AddJustFloatTail
** 功    能 : 给 justfloat 帧添加协议字段 0x00, 0x00, 0x80, 0x7f
** 入口参数 :
**           tx_data : justfloat 帧数据部分
** 出口参数 : 无
*******************************************************************************/
void AddJustFloatTail(uint8_t tx_data[])
{
    // 加入 justfloat 协议 0x00, 0x00, 0x80, 0x7f
    tx_data[0] = 0x00;
    tx_data[1] = 0x00;
    tx_data[2] = 0x80;
    tx_data[3] = 0x7f;
}

/*******************************************************************************
** 名    称 : SendAllToVofa
** 功    能 : 使用 USART1 将传感器数据发送到 VOFA (justfloat 协议)
** 注    意 : 如果要发送所有数据，尽量用这个函数，内部进行了特殊优化
** 入口参数 :
**           icm_data : icm 数据结构体，定义待发送数据，数据长度
**           bmp_data : bmp 数据结构体，定义待发送数据，数据长度
** 出口参数 : 无
*******************************************************************************/
void SendAllToVofa(ICM_Data_t icm_data, BMP280_Data_t bmp_data)
{
    // 【TODO】：封装
    // 【TODO】：设计好 VOFA 结构体
    // 【TODO】：使用动态分配内存或者常量，撤销宏定义
    // 初始化代发送的数据
    uint8_t tx_data[800] = {0xFF};
    // FloatToHex 测试用例： float 0.565 3F10A3D7
    // 发送的数据
    FloatToHex(icm_data.acc[0], tx_data + 0);
    FloatToHex(icm_data.acc[1], tx_data + 4);
    FloatToHex(icm_data.acc[2], tx_data + 8);
    FloatToHex(icm_data.gyro[0], tx_data + 12);
    FloatToHex(icm_data.gyro[1], tx_data + 16);
    FloatToHex(icm_data.gyro[2], tx_data + 20);
    FloatToHex(bmp_data.pressure, tx_data + 24);
    FloatToHex(icm_data.frame_diff, tx_data + 28);
    FloatToHex(icm_data.frame_count, tx_data + 32);
    // justfloat 协议 0x00, 0x00, 0x80, 0x7f
    AddJustFloatTail(tx_data + 36);
    // 使用 DMA UART 发送数据到 VOFA
    SendDataUART(tx_data, 40);
}

/*******************************************************************************
** 名    称 : Send32BitToVofa
** 功    能 : 使用 USART1 将 32 bit float 数据 发送到 VOFA ，仅仅发送一个 (justfloat 协议)
** 入口参数 :
**           bytes : 32 bit float 数据
** 出口参数 : 无
*******************************************************************************/
void Send32BitToVofa(float bytes)
{
    // 注意 tx_data 长度计算
    uint8_t tx_data[8] = {0xFF};
    FloatToHex(bytes, tx_data + 0);
    AddJustFloatTail(tx_data + 4);
    SendDataUART(tx_data, 8);
}

/*******************************************************************************
** 名    称 : Send32BitToVofa
** 功    能 : 使用 USART1 将 一组 float 数据 发送到 VOFA (justfloat 协议)
** 入口参数 :
**           pbuf : 32 bit float 数据数组
**           len  : 数组长度, max VOFA_DATA_LEN/4
** 出口参数 : 无
*******************************************************************************/
void SendDataToVofa(float *pbuf, uint8_t len)
{
    uint8_t tx_buf[VOFA_DATA_LEN] = {0xFF};
    for (int i = 0; i < len; i++)
    {
        FloatToHex(pbuf[i], tx_buf + i * 4);
    }
    AddJustFloatTail(tx_buf + len * 4);
    SendDataUART(tx_buf, len * 4 + 4);
}
/********************************END OF FILE***********************************/
